'    Copyright 2008 Jan Molnar <jan.molnar@abds.de>
'
'    This file is part of JCQ.

'    JCQ is free software: you can redistribute it and/or modify
'    it under the terms of the GNU General Public License as published by
'    the Free Software Foundation, either version 2 of the License, or
'    (at your option) any later version.

'    JCQ is distributed in the hope that it will be useful,
'    but WITHOUT ANY WARRANTY; without even the implied warranty of
'    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'    GNU General Public License for more details.

'    You should have received a copy of the GNU General Public License
'    along with JCQ. If not, see <http://www.gnu.org/licenses/>.

Imports System.ComponentModel
Imports System.Xml

Public Class IcqDataWarehouse
    Inherits ContextService
    Implements Interfaces.IDataWarehouseService

    Public Sub New(ByVal context As IcqContext)
        MyBase.New(context)
    End Sub

    Public Sub Load(ByVal path As System.IO.DirectoryInfo) Implements Interfaces.IDataWarehouseService.Load
        Dim fiContactListData As System.IO.FileInfo
        Dim fiContactListInfo As System.IO.FileInfo
        Dim serializer As JCsTools.Xml.Formatter.XmlSerializer
        Dim dataReader As XmlTextReader
        Dim infoReader As XmlTextReader

        Dim svStorage As Interfaces.IStorageService

        Dim info As ContactListInfo

        fiContactListData = New System.IO.FileInfo(System.IO.Path.Combine(path.FullName, "contactlistdata.xml"))
        If Not fiContactListData.Exists Then Return

        serializer = New JCsTools.Xml.Formatter.XmlSerializer
        serializer.RegisterReferenceFormatter(GetType(IcqContact), New BaseStorageItemFormatter(Context, serializer, GetType(IcqContact)))
        serializer.RegisterReferenceFormatter(GetType(Core.KeyedNotifiyingCollection(Of String, Interfaces.IContact)), New ContactKeyedNotifiyingCollectionFormatter(serializer))
        serializer.RegisterReferenceFormatter(GetType(List(Of Byte)), New JCsTools.Xml.Formatter.ListOfByteFormatter(serializer))

        Dim items As IEnumerable(Of Interfaces.IContact)

        Using fs = fiContactListData.OpenRead
            dataReader = New System.Xml.XmlTextReader(fs)
            dataReader.WhitespaceHandling = WhitespaceHandling.None

            items = DirectCast(serializer.Deserialize(dataReader), IEnumerable(Of Interfaces.IContact))
        End Using

        For Each x As IcqContact In items
            Context.GetService(Of Interfaces.IStorageService).AttachContact(x, x.Group, False)
        Next

        fiContactListInfo = New System.IO.FileInfo(System.IO.Path.Combine(path.FullName, "contactlistinfo.xml"))
        If Not fiContactListInfo.Exists Then Return

        serializer = New JCsTools.Xml.Formatter.XmlSerializer
        serializer.RegisterReferenceFormatter(GetType(ContactListInfo), New ContactListInfoFormatter(serializer))

        Using fs = fiContactListInfo.OpenRead
            infoReader = New System.Xml.XmlTextReader(fs)
            infoReader.WhitespaceHandling = WhitespaceHandling.None

            info = DirectCast(serializer.Deserialize(infoReader), ContactListInfo)
        End Using

        svStorage = Context.GetService(Of Interfaces.IStorageService)()
        svStorage.RegisterLocalContactList(info.ItemCount, info.DateChanged)
    End Sub

    Public Sub Save(ByVal path As System.IO.DirectoryInfo) Implements Interfaces.IDataWarehouseService.Save
        Dim fiContactListData As System.IO.FileInfo
        Dim fiContactListInfo As System.IO.FileInfo
        Dim serializer As JCsTools.Xml.Formatter.XmlSerializer

        Dim dataWriter As XmlTextWriter
        Dim infoWriter As XmlTextWriter

        Dim svStorage As Interfaces.IStorageService

        svStorage = Context.GetService(Of Interfaces.IStorageService)()

        fiContactListData = New System.IO.FileInfo(System.IO.Path.Combine(path.FullName, "contactlistdata.xml"))

        serializer = New JCsTools.Xml.Formatter.XmlSerializer
        serializer.RegisterReferenceFormatter(GetType(IcqContact), New BaseStorageItemFormatter(Context, serializer, GetType(IcqContact)))
        serializer.RegisterReferenceFormatter(GetType(List(Of Byte)), New JCsTools.Xml.Formatter.ListOfByteFormatter(serializer))

        Using fs = New System.IO.FileStream(fiContactListData.FullName, IO.FileMode.Create, IO.FileAccess.Write)
            dataWriter = New System.Xml.XmlTextWriter(fs, System.Text.Encoding.UTF8)

            serializer.Serialize(svStorage.Contacts, dataWriter)

            dataWriter.Flush()
        End Using

        If svStorage.Info Is Nothing Then Return

        fiContactListInfo = New System.IO.FileInfo(System.IO.Path.Combine(path.FullName, "contactlistinfo.xml"))

        serializer = New JCsTools.Xml.Formatter.XmlSerializer
        serializer.RegisterReferenceFormatter(GetType(ContactListInfo), New ContactListInfoFormatter(serializer))

        Using fs = New System.IO.FileStream(fiContactListInfo.FullName, IO.FileMode.Create, IO.FileAccess.Write)
            infoWriter = New System.Xml.XmlTextWriter(fs, System.Text.Encoding.UTF8)

            serializer.Serialize(svStorage.Info, infoWriter)

            infoWriter.Flush()
        End Using
    End Sub

    Public Sub Clear(ByVal path As System.IO.DirectoryInfo) Implements Interfaces.IDataWarehouseService.Clear
        Dim items As IO.FileInfo()

        items = path.GetFiles("contactlist*.xml")

        For Each item As IO.FileInfo In items
            item.Delete()
        Next
    End Sub
End Class

Public Class ContactKeyedNotifiyingCollectionFormatter
    Inherits JCsTools.Xml.Formatter.DefaultIListReferenceFormatter

    Private ReadOnly _Contacts As Core.NotifyingCollection(Of Interfaces.IContact)
    Private ReadOnly _Groups As Core.NotifyingCollection(Of Interfaces.IGroup)

    Public Sub New(ByVal parent As JCsTools.Xml.Formatter.XmlSerializer)
        MyBase.New(parent, GetType(ContactKeyedNotifiyingCollectionFormatter))

        _Contacts = New Core.NotifyingCollection(Of Interfaces.IContact)
        _Groups = New Core.NotifyingCollection(Of Interfaces.IGroup)
    End Sub

    Protected Overrides Function CreateObject(ByVal type As System.Type, ByVal reader As XmlReader) As Object
        Dim coll = New Core.KeyedNotifiyingCollection(Of String, Interfaces.IContact)(Function(c) c.Identifier)

        Return coll
    End Function
End Class

Public Class ContactListInfoFormatter
    Inherits JCsTools.Xml.Formatter.DefaultReferenceFormatter

    Public Sub New(ByVal parent As JCsTools.Xml.Formatter.XmlSerializer)
        MyBase.New(parent, GetType(ContactListInfo))
    End Sub

    Protected Overrides Function CreateObject(ByVal type As System.Type, ByVal reader As XmlReader) As Object
        Dim itemCountValue As String
        Dim dateChangedValue As String

        Dim itemCount As Integer
        Dim dateChanged As Date

        itemCountValue = reader.GetAttribute("itemCount")
        dateChangedValue = reader.GetAttribute("dateChanged")

        itemCount = XmlConvert.ToInt32(itemCountValue)
        dateChanged = XmlConvert.ToDateTime(dateChangedValue, XmlDateTimeSerializationMode.Utc)

        Return New ContactListInfo(itemCount, dateChanged)
    End Function

    Protected Overrides Sub SerializeProperties(ByVal graph As Object, ByVal writer As System.Xml.XmlWriter)
        Dim info As ContactListInfo

        info = DirectCast(graph, ContactListInfo)

        writer.WriteAttributeString("itemCount", XmlConvert.ToString(info.ItemCount))
        writer.WriteAttributeString("dateChanged", XmlConvert.ToString(info.DateChanged, XmlDateTimeSerializationMode.Utc))
    End Sub

    Protected Overrides Sub DeserializeProperties(ByVal graph As Object, ByVal reader As System.Xml.XmlReader)
        ' We need to override this method since the base implementation does deserialize
        ' the properties and tries to assign them to the graph.
        ' Since this object has only ReadOnly properties which already were set by the
        ' CreateObject method, there is nothing to do here.
    End Sub
End Class

Public Enum FormatTarget
    Attribute
    Element
End Enum